package com.yang.usb;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.AssetManager;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.method.ScrollingMovementMethod;
import android.text.style.ForegroundColorSpan;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;


import com.yang.usb.util.FileUtil;
import com.yang.usbserial.driver.UsbSerialDriver;
import com.yang.usbserial.driver.UsbSerialPort;
import com.yang.usbserial.driver.UsbSerialProber;
import com.yang.usbserial.util.HexDump;
import com.yang.usbserial.util.LogUtils;
import com.yang.usbserial.util.SerialInputOutputManager;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * A simple {@link Fragment} subclass.
 * Use the {@link BlankFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class BlankFragment extends Fragment implements SerialInputOutputManager.Listener {
    private List<byte[]> binFiles = new ArrayList<>();

    private enum UsbPermission {Unknown, Requested, Granted, Denied}

    private UsbPermission usbPermission = UsbPermission.Unknown;
    private final BroadcastReceiver broadcastReceiver;
    private static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB";

    private final Handler mainLooper;
    private int deviceId, portNum, baudRate;
    private boolean withIoManager;
    private TextView receiveText;
    private ProgressBar progressBar;
    private Button sendBtn;
    private UsbSerialPort usbSerialPort;
    private SerialInputOutputManager usbIoManager;

    /**
     * 是否连接
     */
    private boolean connected = false;

    private static final int WRITE_WAIT_MILLIS = 2000;
    private static final int READ_WAIT_MILLIS = 2000;

    private byte[] currentFileData; // 当前文件数据
    private int currentOffset = 0; // 当前偏移

    private int currentFileIndex = 1;
    private static final int CHUNK_SIZE = 1024;

    private byte[] lastSentDataBlock; // 保存上一次发送的数据块
    /**
     * 是否在发当前文件数据
     */
    private boolean isSendingData = false;

    /**
     * 所有文件总大小
     */
    private int totalsize = 0;
    /**
     * 发送文件长度
     */
    private int currentfilelength = 0;


    public BlankFragment() {
        broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (INTENT_ACTION_GRANT_USB.equals(intent.getAction())) {
                    usbPermission = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
                            ? UsbPermission.Granted : UsbPermission.Denied;
                    connect();
                }
            }
        };
        mainLooper = new Handler(Looper.getMainLooper());
    }

    /*
     * Lifecycle
     */
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        setRetainInstance(true);
        deviceId = getArguments().getInt("device");
        LogUtils.e(deviceId + "");
        portNum = getArguments().getInt("port");
        LogUtils.e(portNum + "");
        baudRate = getArguments().getInt("baud");
        LogUtils.e(baudRate + "");
        withIoManager = getArguments().getBoolean("withIoManager");
        LogUtils.e(withIoManager + "");
    }

    @Override
    public void onResume() {
        super.onResume();
        getActivity().registerReceiver(broadcastReceiver, new IntentFilter(INTENT_ACTION_GRANT_USB));

        if (usbPermission == UsbPermission.Unknown || usbPermission == UsbPermission.Granted)
            mainLooper.post(this::connect);


//        binFiles= FileUtil.readBinFilesFromAssets(getContext());
        //获取Download文件夹下的bin文件
        binFiles = FileUtil.readFilesFromDownloadFolder();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_flie, container, false);
        // Inflate the layout for this fragment
        receiveText = view.findViewById(R.id.receive_text);                          // TextView performance decreases with number of spans
        receiveText.setTextColor(getResources().getColor(R.color.colorRecieveText)); // set as default color to reduce number of spans
        receiveText.setMovementMethod(ScrollingMovementMethod.getInstance());
        sendBtn = view.findViewById(R.id.send_btn);
        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendBtn.setVisibility(View.GONE);
                send(buildCommand());
            }
        });
        progressBar = view.findViewById(R.id.progressBar);
        progressBar.setProgress(0);
        return view;
    }


    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_terminal, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.clear) {
            receiveText.setText("");
            return true;
        } else if (id == R.id.send_break) {
            if (!connected) {
                Toast.makeText(getActivity(), "not connected", Toast.LENGTH_SHORT).show();
            } else {
                try {
                    usbSerialPort.setBreak(true);
                    Thread.sleep(100); // should show progress bar instead of blocking UI thread
                    usbSerialPort.setBreak(false);
                    SpannableStringBuilder spn = new SpannableStringBuilder();
                    spn.append("send <break>\n");
                    spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorSendText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
                    receiveText.append(spn);
                } catch (UnsupportedOperationException ignored) {
                    Toast.makeText(getActivity(), "BREAK not supported", Toast.LENGTH_SHORT).show();
                } catch (Exception e) {
                    Toast.makeText(getActivity(), "BREAK failed: " + e.getMessage(), Toast.LENGTH_SHORT).show();
                }
            }
            return true;
        } else {
            return super.onOptionsItemSelected(item);
        }
    }

    /***
     * 设备连接
     */
    private void connect() {
        LogUtils.e("设备连接");
        UsbDevice device = null;
        UsbManager usbManager = (UsbManager) getActivity().getSystemService(Context.USB_SERVICE);
        for (UsbDevice v : usbManager.getDeviceList().values())
            if (v.getDeviceId() == deviceId)
                device = v;
        if (device == null) {
            status("连接失败：找不到设备");
            return;
        }
        UsbSerialDriver driver = UsbSerialProber.getDefaultProber().probeDevice(device);
        if (driver == null) {
            driver = CustomProber.getCustomProber().probeDevice(device);
        }
        if (driver == null) {
            status("连接失败：没有设备的驱动程序");
            return;
        }
        if (driver.getPorts().size() < portNum) {
            status("连接失败：设备上没有足够的端口");
            return;
        }
        usbSerialPort = driver.getPorts().get(portNum);
        UsbDeviceConnection usbConnection = usbManager.openDevice(driver.getDevice());
        if (usbConnection == null && usbPermission == UsbPermission.Unknown && !usbManager.hasPermission(driver.getDevice())) {
            usbPermission = UsbPermission.Requested;
            int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ? PendingIntent.FLAG_MUTABLE : 0;
            PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(getActivity(), 0, new Intent(INTENT_ACTION_GRANT_USB), flags);
            usbManager.requestPermission(driver.getDevice(), usbPermissionIntent);
            return;
        }
        if (usbConnection == null) {
            if (!usbManager.hasPermission(driver.getDevice()))
                status("连接失败：权限被拒绝");
            else
                status("连接失败：打开失败");
            return;
        }
        try {
            usbSerialPort.open(usbConnection);
            try {
                usbSerialPort.setParameters(baudRate, 8, 1, UsbSerialPort.PARITY_NONE);
            } catch (UnsupportedOperationException e) {
                status("不支持设置参数");
            }
            if (withIoManager) {
                usbIoManager = new SerialInputOutputManager(usbSerialPort, this);
                usbIoManager.start();
            }
            status("连接成功");
            connected = true;
//            controlLines.start();
        } catch (Exception e) {
            status("connection failed: " + e.getMessage());
            disconnect();
        }
    }

    @Override
    public void onNewData(byte[] data) {
        mainLooper.post(() -> {
            int progress = (int) (((float) currentfilelength / totalsize) * 100);
            progressBar.setProgress(progress);
            receive(data);
        });
    }

    @Override
    public void onRunError(Exception e) {
        mainLooper.post(() -> {
            status("connection lost: " + e.getMessage());
            disconnect();
        });
    }


    void status(String str) {
        SpannableStringBuilder spn = new SpannableStringBuilder(str + '\n');
        spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorStatusText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        receiveText.append(spn);
    }

    private void disconnect() {
        connected = false;
        if (usbIoManager != null) {
            usbIoManager.setListener(null);
            usbIoManager.stop();
        }
        usbIoManager = null;
        try {
            if (usbSerialPort != null) {
                usbSerialPort.close();
            }
        } catch (IOException ignored) {
        }
        usbSerialPort = null;
    }

    private void send(String str) {
        if (!connected) {
            Toast.makeText(getActivity(), "not connected", Toast.LENGTH_SHORT).show();
            return;
        }
        SpannableStringBuilder spn = new SpannableStringBuilder();
        spn.append("文件升级中").append("\n");
        try {
            LogUtils.e(str);
            byte[] data = HexDump.hexStringToByteArray(str);
            spn.append("send " + data.length + " bytes\n");
            spn.append(HexDump.dumpHexString(str)).append("\n");
            spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorSendText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            receiveText.append(spn);
            usbSerialPort.write(data, WRITE_WAIT_MILLIS);
        } catch (Exception e) {
            onRunError(e);
        }
    }

    private void sendDataToUSBDevice(byte[] data) {
        LogUtils.e("真正发送的长度" + data.length);
        if (!connected) {
            Toast.makeText(getActivity(), "not connected", Toast.LENGTH_SHORT).show();
            return;
        }
        try {
//            SpannableStringBuilder spn = new SpannableStringBuilder();
//            spn.append("send " + data.length + " bytes\n");
//            spn.append(HexDump.dumpHexString(data)).append("\n");
//            spn.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.colorSendText)), 0, spn.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
//            receiveText.append(spn);
            usbSerialPort.write(data, WRITE_WAIT_MILLIS);
        } catch (Exception e) {
            onRunError(e);
        }
    }

    private void read() {
        if (!connected) {
            Toast.makeText(getActivity(), "not connected", Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            byte[] buffer = new byte[8192];
            int len = usbSerialPort.read(buffer, READ_WAIT_MILLIS);
            receive(Arrays.copyOf(buffer, len));
        } catch (IOException e) {
            // when using read with timeout, USB bulkTransfer returns -1 on timeout _and_ errors
            // like connection loss, so there is typically no exception thrown here on error
            status("connection lost: " + e.getMessage());
            disconnect();
        }
    }

    private void receive(byte[] data) {
//        SpannableStringBuilder spn = new SpannableStringBuilder();
//        spn.append("receive " + data.length + " bytes\n");
//        if (data.length > 0)
//            spn.append(HexDump.dumpHexString(data)).append("\n");
//        receiveText.append(spn);
        if (new String(data).equals("OK")) {
            receiveText.setText("开始发文件");
            if (isSendingData) {
                currentOffset += CHUNK_SIZE; // 数据传输成功，继续下一个数据块
            }
            sendDataChunk();
        } else if (new String(data).equals("ERR")) {
            receiveText.setText("重发数据");
            LogUtils.e("重发数据");
            if (lastSentDataBlock != null) {
                sendDataToUSBDevice(lastSentDataBlock);
            }
        }
    }

    private String buildCommand() {
        // 构建命令，格式为 "USB_Update;文件1的大小;文件2的大小;文件3的大小;"
        StringBuilder commandBuilder = new StringBuilder("USB_Update;");
        for (byte[] binFileData : binFiles) {
            int fileSize = binFileData.length;
            totalsize += fileSize;
            commandBuilder.append(fileSize).append(";");
        }
        LogUtils.e(commandBuilder.toString());
        return HexDump.convertToHexAndSendData(commandBuilder.toString());
    }


    private void sendDataChunk() {
        if (currentFileData == null) {
            // 当前文件数据为空，表示所有文件已经发送完成
            if (currentFileIndex < binFiles.size()) {
                currentOffset = 0;
                currentFileIndex = 0;
                currentFileData = binFiles.get(currentFileIndex);
                isSendingData = true;
            } else {
                return;
            }
        }

        if (currentFileData != null && currentOffset >= currentFileData.length) {
            // 当前文件数据已发送完成，切换到下一个文件
            LogUtils.e("当前文件数据已发送完成，切换到下一个文件");
            currentOffset = 0;
            currentFileIndex++;
            if (currentFileIndex < binFiles.size()) {
                LogUtils.e("下个文件");
                currentFileData = binFiles.get(currentFileIndex);
                currentOffset = 0;
                isSendingData = true;
            } else {
                receiveText.setText("文件发送结束");
                sendBtn.setVisibility(View.VISIBLE);
                LogUtils.e("发送完成");
                currentFileData = null;
                isSendingData = false;
                return;
            }
        }

        if (currentFileData != null) {
            int chunkSize = Math.min(CHUNK_SIZE, currentFileData.length - currentOffset);
            byte[] chunkData = new byte[chunkSize];
            System.arraycopy(currentFileData, currentOffset, chunkData, 0, chunkSize);

            currentfilelength += chunkSize;
            // 计算并发送LRC检验
            byte lrc = calculateLRC(chunkData);
            LogUtils.e("发送过去的 LRC: " + lrc);

            // 合并数据块和LRC
            byte[] dataWithLRC = new byte[chunkSize + 1];
            System.arraycopy(chunkData, 0, dataWithLRC, 0, chunkSize);
            dataWithLRC[chunkSize] = lrc;

            // 保存上一次发送的数据块
            lastSentDataBlock = dataWithLRC;

            // 发送数据
            sendDataToUSBDevice(dataWithLRC);
        }
    }

    private byte calculateLRC(byte[] data) {
        byte lrc = 0;
        for (byte b : data) {
            lrc ^= b;
        }
        return lrc;
    }


}